<template>
  <el-upload action="#" class="upload" :drag="true" :limit="excelLimit" :multiple="true" :show-file-list="false" :before-upload="beforeExcelUpload" :on-exceed="handleExceed"
    :on-success="excelUploadSuccess" :on-error="excelUploadError" :accept="parameter.fileType!.join(',')">
    <slot name="empty">
      <div class="el-upload__text">
        <span>将文件拖到此处，或</span><em>点击上传</em><el-link @click.stop="downloadTemp">下载模板</el-link>
      </div>
    </slot>
  </el-upload>
  <div class="table-box" :class="uploadDisabled ? 'active' : ''">
    <ProTable
      row-key="path"
      ref="proTable"
      :columns="columns"
      :data="tableList"
      :pagination="false"
      :toolButton="false"
    >
      <template #arrangeTimeList="scope">
        <div class="arrange-time-list" v-if="scope.row.arrangeTimeList">
          <div v-for="(item, index) in scope.row.arrangeTimeList.split(',')" :key="index">
            {{ item }}
          </div>
        </div>
      </template>
      <template #resultStatus="scope">
        <el-text class="mx-1" type="success" v-if="scope.row.code === 0">导入成功</el-text>
        <el-text class="mx-1" type="danger" v-else>{{ scope.row.msg }}</el-text>
      </template>
    </ProTable>
    <div class="import-text">
      <div>总条数：<span class="total">{{ tableList.length }}，</span></div>
      <div>已导入：<span class="total">{{ importData.totalCount }}</span> 条（成功：<span class="success">{{ importData.successCount }}</span> 条，失败： <span class="error">{{ importData.errorCount }}</span> 条）</div>
    </div>
  </div>
</template>

<script setup lang="ts" name="ImportExcel">
import { onMounted, ref } from "vue";
import _ from 'lodash';
import * as XLSX from 'xlsx';
import ProTable from '@/components/ProTable/index.vue'
import { ElMessageBox, ElNotification, ElMessage, UploadRawFile } from "element-plus";
import { getImportTemplate } from '@/api/modules/commom'

export interface ExcelParameterProps {
  fileSize?: number; // 上传文件的大小
  scopeStart?: string; // excel模板表头对应的位置
  fileType?: any; // 上传文件的类型
  templateId: string; // 下载模版的参数
  versionCell?: string; // 版本号所在的列
  tempApi?: (params: any) => Promise<any>; // 下载模板的Api
  importApi?: (params: any) => Promise<any>; // 批量导入的Api
  importDataCallback?: (data: any) => void; // 数据回调
  getTableList?: () => void; // 获取表格数据的Api
  importParams?: any; // 导入参数
  columns: any; // excel表头名称对应后端接口需要的key { 学生姓名: 'child_name' }
}
// 父组件传过来的参数
const parameter = withDefaults(defineProps<ExcelParameterProps>(), {
  fileSize: 20,
  scopeStart: 'A3',
  versionCell: 'B2',
  tempApi: getImportTemplate,
  fileType: ["application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],
  columns: () => [],
  importParams: () => ({})
})
// 最大文件上传数
const excelLimit = ref(1);
/** 原始数据，筛选需要 */
const tableData = ref<any>([])
/** 列表数据 */
const tableList = ref<any>([])
const uploadDisabled = ref<boolean>(false)
const importData = ref<any>({
  totalCount: 0,
  errorCount: 0,
  successCount: 0
})
const templateInfo = ref<any>({
  version: '',
  name: '',
  url: ''
})
const getTemplateApi = async () => {
  const { data } = await parameter.tempApi({ templateId: parameter.templateId })
  templateInfo.value = data
}
// Excel 导入模板下载
const downloadTemp = () => {
  if (!parameter.tempApi) return;
  try {
    const { version, name, url } = templateInfo.value
    const exportFile = document.createElement("a");
    exportFile.style.display = "none";
    exportFile.download = `${name}.xlsx`;
    exportFile.href = url;
    document.body.appendChild(exportFile);
    exportFile.click();
    // 去除下载对 url 的影响
    document.body.removeChild(exportFile);
    window.URL.revokeObjectURL(url);
    return version
  } catch (error) {
    console.log(error);
  }
};
const checkVersion = (sheet: any) => {
  const versionList = []
  sheet['!ref'] = parameter.versionCell + ':' + parameter.versionCell
  const range = XLSX.utils.decode_range(sheet['!ref'])
  let C
  const R = range.s.r
  for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */
    var cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })] /* find the cell in the first row */
    var hdr = 'UNKNOWN ' + C // <-- replace with your desired default
    if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
    versionList.push(hdr)
  }
  const v = versionList[0]
  if (v !== templateInfo.value.version) {
    return false
  }

  return true
}
/**
 * @description 文件上传之前判断
 * @param file 上传的文件
 * */
const emit = defineEmits<{
  (e: 'uploadInit', value: boolean): void
}>()
const beforeExcelUpload = (file: UploadRawFile) => {
  const isExcel = parameter.fileType!.includes(file.type as File.ExcelMimeType);
  const fileSize = file.size / 1024 / 1024 < parameter.fileSize!;
  if (!isExcel)
    ElNotification({
      title: "温馨提示",
      message: "上传文件只能是 xls / xlsx 格式！",
      type: "warning"
    });
  if (!fileSize)
    setTimeout(() => {
      ElNotification({
        title: "温馨提示",
        message: `上传文件大小不能超过 ${parameter.fileSize}MB！`,
        type: "warning"
      });
    }, 0);
  if (isExcel && fileSize) {
    emit('uploadInit', false)
    const reader = new FileReader();
    reader.onload = () => {
      const data = reader.result as string; // 类型断言，告诉 TS 读取的结果是字符串
      const workbook: any = XLSX.read(data, { type: 'binary' });
      // 解析第一个工作表的数据
      const sheetName: any = workbook.SheetNames[0];
      const sheet = workbook.Sheets[sheetName];
      if (templateInfo.value.version && !checkVersion(_.cloneDeep(workbook.Sheets[sheetName]))) {
        ElMessageBox.alert('表格模板错误，请使用最新模板导入（最新模板可通过“下载模板”下载）', '温馨提醒', { type: 'warning' })
        return
      }
      if (parameter.scopeStart) {
        workbook.Sheets[workbook.SheetNames[0]]['!ref'] = workbook.Sheets[workbook.SheetNames[0]]['!ref'].replace('A1', parameter.scopeStart)
      }
      const jsonData = XLSX.utils.sheet_to_json(sheet);
      tableList.value = tableData.value = formatData(jsonData, parameter.columns)
    };
    reader.readAsBinaryString(file);
  }
  return false;
};
// 数据格式化处理函数
function formatData(rawData: any[], columns: any[]) {
  return rawData.map((item) => {
    const formattedItem: Record<string, any> = {};
    columns.forEach((col: any) => {
      if (item[col.colName]) {
        formattedItem[col.prop] = item[col.colName];
      }
    });
    return formattedItem;
  });
}
// 文件数超出提示
const handleExceed = () => {
  ElNotification({
    title: "温馨提示",
    message: "最多只能上传一个文件！",
    type: "warning"
  });
};

// 上传错误提示
const excelUploadError = () => {
  ElNotification({
    title: "温馨提示",
    message: `导入失败，请您重新上传！`,
    type: "error"
  });
};

// 上传成功提示
const excelUploadSuccess = () => {
  ElNotification({
    title: "温馨提示",
    message: `导入成功！`,
    type: "success"
  });
};

/** 过滤筛选 */
const importDataFilter = (value: any) => {
  if (value === undefined) {
    tableList.value = _.cloneDeep(tableData.value)
    return
  }
  const list = tableData.value.filter((item: any) => {
    return item.code == value
  })
  tableList.value = list
}
// 处理导入以后的回显状态
const importDataFunc = (data: any) => {
  const { list } = data
  importData.value = data
  uploadDisabled.value = true
  const table = tableData.value.map((item: any, index: number) => {
    const { code, msg } = list[index]
    return {
      ...item,
      code,
      msg
    }
  })
  tableList.value = tableData.value = table
}
/** 批量导入 */
const batchImport = async () => {
  if (!parameter.importApi) return;
  if (!tableData.value.length) {
    ElMessageBox.alert('导入学生列表数据不能为空', '温馨提醒', {
      type: 'warning',
      confirmButtonText: '好的',
      callback: () => {},
    })
    return
  }
  let list = tableData.value
  if (parameter.importDataCallback) {
    list = parameter.importDataCallback(tableData.value)
  }
  
  // 分页导入数据，每次请求50条
  const pageSize = 50
  const totalCount = list.length
  const totalPages = Math.ceil(totalCount / pageSize)
  
  let allResults: any = {
    list: [],
    errorCount: 0,
    successCount: 0,
    totalCount: 0
  }
  
  for (let page = 0; page < totalPages; page++) {
    const startIndex = page * pageSize
    const endIndex = Math.min(startIndex + pageSize, totalCount)
    const pageData = list.slice(startIndex, endIndex)
    const { data } = await parameter.importApi({ list: pageData, ...parameter.importParams })
    
    // 合并每次请求的结果
    if (data && data.list) {
      allResults.list = [...allResults.list, ...data.list]
    }
    allResults.errorCount += data.errorCount
    allResults.successCount += data.successCount
    allResults.totalCount += data.totalCount
    // 可以添加进度提示
    ElMessage({
      type: 'info',
      message: `正在导入数据：${Math.min((page + 1) * pageSize, totalCount)}/${totalCount}`,
      duration: 1000
    })
  }
  importDataFunc(allResults)
}
onMounted(() => {
  getTemplateApi()
})
// 暴露给父组件的参数和方法 (外部需要什么，都可以从这里暴露出去)
defineExpose({
  tableData,
  importDataFilter,
  batchImport
});
</script>
<style lang="scss" scoped>
.table-box {
  padding-top: 10px;
  max-height: calc(100vh - 270px);
  &.active {
    max-height: calc(100vh - 215px);
  }
}
:deep(.el-upload-dragger) {
  padding: 16px;
  .el-upload__text {
    display: flex;
    justify-content: center;
    align-items: center;
    .el-link {
      margin-left: 10px;
      color: #2c3146;
    }
  }
}
.import-text {
  display: flex;
  font-size: 15px;
  text-align: left;
  padding: 20px 0;
  color: #666;
  span {
    font-weight: bold;
    &.success {
      color: #67c23a;
    }
    &.error {
      color: red;
    }
  }
}
</style>
